package wibrst.utils{
	import com.greensock.TweenMax;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.TextField;
	import wibrst.visual.debug.VRegMark;
	/**
	 * ...
	 * @author wibrst@qq.com
	 */
	public class VisualUtil {
		
		static public function removeAllChildren( cont:DisplayObjectContainer ):void {
			if( cont ) for ( ; cont.numChildren; cont.removeChildAt ( 0 ) ) { };
		}
		
		static public function getDiagInfo( v:DisplayObject ):String {
			var sOut:String = "\nVisualUtil.getDiagInfo of " + v.toString();
			var _vcProp:Vector.<String> = new Vector.<String>;
			_vcProp.push( "visible", "alpha", "width", "height", "x", "y", "parent", "root", "stage" );
			if( v is DisplayObjectContainer )
				_vcProp.push( "numChildren" );
			for ( var i:Number = 0; i < _vcProp.length; ++i ) {
				var sProp:String = _vcProp[i];
				sOut += "\n\t\t" + sProp + "\t:" + v[ sProp ];
			}
			return sOut;
		}
		
		static public function getRectOf( visual:DisplayObject ):Rectangle {
			var rect:Rectangle = new Rectangle( visual.x, visual.y, visual.width, visual.height );
			return rect;
		}
		
		/// effect
		
		static public function fadeAddChild( vNewChild:DisplayObject, vAddCont:DisplayObjectContainer,
				vOldChild:DisplayObject = null , nDur:Number = .5, funcComp:Function = null
		):void {
			//trace*( "[CommUtils] fadeAddChild:", arguments );
			if ( vOldChild && vOldChild.parent ) {
				fadeRemoveChild( vOldChild, nDur );
			}
			
			vNewChild.alpha = 0;
			vNewChild.visible = true;
			vAddCont.addChild( vNewChild );
			
			var oTween:Object = new Object;
			oTween.alpha = 1;
			if ( funcComp is Function ) oTween.onComplete = funcComp;
			TweenMax.to( vNewChild, nDur, oTween );
		}
		
		static public function fadeRemoveChild( vOldChild:DisplayObject, nDur:Number = .5, funcComp:Function = null ):void {
			//trace*( "[CommUtils] fadeRemoveChild:", arguments );
			TweenMax.to( vOldChild, nDur, { alpha:0, onComplete:fadeRemoveChildEndHandler, onCompleteParams:[vOldChild, funcComp] } );
		}
		
		static private function fadeRemoveChildEndHandler( vOldChild:DisplayObject, funcComp:Function ):void{
			if ( vOldChild.parent ) vOldChild.parent.removeChild( vOldChild );
			if ( funcComp is Function ) funcComp.apply();
		}
		
		/// locate
		
		static public function setDisplayCenterPoint( disp:DisplayObject, pCenter:Point = null ):void {
			if ( ! pCenter ) pCenter = new Point;
			var rectBounds:Rectangle = disp.getBounds( disp );
			//trace( "setDisplayCenterPoint", disp, rectBounds, pCenter );
			disp.x = pCenter.x - (rectBounds.x + rectBounds.width / 2);
			disp.y = pCenter.y - (rectBounds.y + rectBounds.height / 2);
		}
		
		/*static public function setVisualCentre( visual:DisplayObject ):void {
			
		}*/
		
		static public function setDisplayCenterPointOfParent( disp:DisplayObject, parent:DisplayObjectContainer, pCenter:Point = null ):void {
			if ( ! pCenter ) pCenter = new Point;
			var rectBounds:Rectangle = disp.getBounds(disp.parent);
			//trace( "CommUtils.setDisplayCenterPointOfParent:", rectBounds );
			disp.x += pCenter.x - (rectBounds.x + rectBounds.width / 2);
			disp.y += pCenter.y - (rectBounds.y + rectBounds.height / 2);
		}
		
		static public function setTxCenterToPoint( tx:TextField, pnt:Point ):void {
			var rect:Rectangle = getTextFieldBoundaries( tx );
			tx.x = pnt.x - rect.x - rect.width / 2;
			tx.y = pnt.y - rect.y - rect.height / 2;
		}
		
        static public function getTextFieldBoundaries( tx:TextField ):Rectangle {
            var rectChar:Rectangle  = new Rectangle;
            var rectChars:Rectangle = rectChar;
            for (var i:Number = 0; i < tx.length;++i) {
                rectChar = tx.getCharBoundaries(i);
				/*if ( i > 9 ) {	/// debug
					//trace*( "[CommUtils]getTextFieldBoundaries line:", tx.getLineIndexOfChar(i), "idx:", i,
							"bounds:", rectChar );
				}*/
                if (i == 0) {
                    rectChars = rectChar;
                }else {
                    if(rectChar) rectChars = rectChars.union(rectChar);
                }
            }
            return rectChars;
        }
		
        static public function getTxBoundarySingle( tx:TextField ):Rectangle {
            var rectChars:Rectangle = new Rectangle;
			rectChars = tx.getCharBoundaries ( 0 ) ;
			rectChars.union ( tx.getCharBoundaries ( tx.length - 1 ) ) ;
            return rectChars;
        }
		
		static public function addChildUnder( dispNew:DisplayObject, dispUnder:DisplayObject ):void {
			if ( dispNew && dispUnder && dispUnder.parent ) {
				var p:DisplayObjectContainer = dispUnder.parent;
				p.addChildAt( dispNew, p.getChildIndex( dispUnder ) );
			}
		}
		
		static public function autoRemoveChild( disp:DisplayObject ):void {
			if ( disp){
				if ( disp.parent ) disp.parent.removeChild( disp );
				if ( disp is MovieClip ) {
					( disp as MovieClip ).gotoAndStop( 1 );
				}
			}
		}
		
		static public function getNativeRect ( vTgt:DisplayObject ):Rectangle {
			return vTgt.getBounds( vTgt );
		}
		
		static public function getParentRect ( vTgt:DisplayObject ):Rectangle {
			return vTgt.getBounds( vTgt.parent );
		}
		
		static public function getFitRect ( rcDispNt:Rectangle, rcFrame:Rectangle, bFill:Boolean = false ):Rectangle {
			var rcAdjust:Rectangle = new Rectangle;
			
			var nFitScale:Number;
			var nW2hScaleFrame:Number = rcFrame.width / rcFrame.height;
			var nW2hScaleDisp:Number = rcDispNt.width / rcDispNt.height;
			
			if( Boolean( Number(nW2hScaleDisp > nW2hScaleFrame) ^ Number(!bFill) ) ){
				nFitScale = rcFrame.height / rcDispNt.height;			/// according height
				rcAdjust.height = rcFrame.height;
				rcAdjust.width = rcDispNt.width * nFitScale;
				rcAdjust.x = rcFrame.x + ( rcFrame.width - rcAdjust.width ) / 2 - rcDispNt.x * nFitScale;
				rcAdjust.y = rcFrame.y - rcDispNt.y * nFitScale;
			}else{
				nFitScale = rcFrame.width / rcDispNt.width;				/// according width
				rcAdjust.width = rcFrame.width;
				rcAdjust.height = rcDispNt.height * nFitScale;
				rcAdjust.x = rcFrame.x - rcDispNt.x * nFitScale;
				rcAdjust.y = rcFrame.y + ( rcFrame.height - rcAdjust.height ) / 2 - rcDispNt.y * nFitScale;
			}
			
			return rcAdjust;
		}
	
		/// IMPORTANT
		static public function fitDispOnDisp( vTgt:DisplayObject, vFrame:DisplayObject, bFill:Boolean = false,
				bReviseScale:Boolean = true, bReviseSkew:Boolean = true, vCase:DisplayObjectContainer = null ):void {
			fitDispOnRect( vTgt, getParentRect( vFrame ), bFill, bReviseScale, bReviseSkew, vCase );
		}
		
		static public function fitDispOnRect( vTgt:DisplayObject, rcFrame:Rectangle, bFill:Boolean = false,
				bReviseScale:Boolean = true, bReviseSkew:Boolean = true, vCase:DisplayObjectContainer = null ):void {
			if (bReviseScale) reviseScale( vTgt );
			if (bReviseSkew) reviseSkew( vTgt );
			
			var rectBounds:Rectangle = getFitRect( getNativeRect( vTgt ), rcFrame, bFill );
			
			vTgt.x = rectBounds.x;
			vTgt.y = rectBounds.y;
			vTgt.width = rectBounds.width;
			vTgt.height = rectBounds.height;
			
			if ( vCase ) vCase.addChild( vTgt );
		}
		
		static public function reviseScale( vTgt:DisplayObject ):void {
			//trace( "VisualUtil.reviseScale~~~" );
			var bChanged:Boolean = false;
			var mtx:Matrix = vTgt.transform.matrix;
			if ( mtx.a < 0 ) { mtx.a *= -1;	bChanged = true }	/// scale x revise
			if ( mtx.d < 0 ) { mtx.d *= -1;	bChanged = true }	/// scale y revise
			if (bChanged) vTgt.transform.matrix = mtx;
		}
		
		static public function reviseSkew( vTgt:DisplayObject ):void {
			//trace( "VisualUtil.reviseSkew~~~" );
			var bChanged:Boolean = false;
			var mtx:Matrix = vTgt.transform.matrix;
			if ( mtx.c != 0 ) { mtx.c = 0;	bChanged = true }	/// skew x revise
			if ( mtx.b != 0 ) { mtx.b = 0;	bChanged = true }	/// skew y revise
			if (bChanged) vTgt.transform.matrix = mtx;
		}
		
		static public function setHotAreaBack( dispCont:DisplayObjectContainer , alpha:Number = .0 ):void {
				var rect:Rectangle = dispCont.getBounds( dispCont );
				
				var hotArea:Sprite = new Sprite;
				hotArea.graphics.beginFill( 0x00ff00, alpha );
				hotArea.graphics.drawRect( rect.x, rect.y, rect.width, rect.height );
				hotArea.graphics.endFill();
				
				dispCont.addChildAt( hotArea, 0 );
		}
		
		/// --- --- --- Bmd operate --- --- --- 
		
		static public function createBmpForTextField( txSrc:TextField, bLocateWith:Boolean=true ):Bitmap {
			var bmp:Bitmap = new Bitmap;
			if( bLocateWith ){
				bmp.x = txSrc.x;
				bmp.y = txSrc.y;
			}
			return (bmp);
		}
		
		static public function updateBmpForTextField( txSrc:TextField, bmp:Bitmap ):void {
			var bmd:BitmapData = new BitmapData(txSrc.width, txSrc.height, true, 0x00);
			bmd.draw( txSrc, new Matrix(1,0,0,1,0,3) );
			bmp.bitmapData = bmd;
		}
		
		static public function getBmdCopyOfDisp( dispSrc:DisplayObject , clipRect:Rectangle = null, bDebugClr:Boolean = true ):BitmapData {
			//trace( "CommUtils::getBmdCopyOfDisp ~~~ dispSrc.transform.matrix:\n\t", dispSrc.transform.matrix );
            var rcInPr:Rectangle = clipRect ? clipRect : dispSrc.getBounds( dispSrc.parent );
			
			var bmd:BitmapData = new BitmapData(  rcInPr.width,  rcInPr.height, true, bDebugClr ? 0x8800ff00 : 0x0000ff00 );
			
			/// [[ mask hack before draw ]]
			var maskDump:DisplayObject;
			if ( dispSrc.mask ) {
				maskDump = dispSrc.mask;
				dispSrc.mask = null;
			}
			/// [[ mask hack before draw ]]
			
			var mtxDup:Matrix = dispSrc.transform.matrix;
			mtxDup.tx = mtxDup.tx - rcInPr.x;
			mtxDup.ty = mtxDup.ty - rcInPr.y;
			bmd.draw( dispSrc, mtxDup );
			//trace( "CommUtils::getBmdCopyOfDisp ~~~ mtxDup:\n\t", mtxDup );
 		
			var vRegMark:VRegMark = new VRegMark;
			var mtxMark:Matrix = new Matrix;
			mtxMark.createBox( 1, 1, 0, mtxDup.tx, mtxDup.ty );
			//mtxMark.tx = mtxDup.tx;
			//mtxMark.ty = mtxDup.ty;
			bmd.draw( vRegMark, mtxMark );
			//trace( "CommUtils::getBmdCopyOfDisp ~~~ mtxMark:\n\t", mtxMark );
			
			/// [[ mask hack after draw ]]
			if ( maskDump )	dispSrc.mask = maskDump;
			/// [[ mask hack before draw ]]
			
			return bmd;
		}
		
	}

}